﻿
namespace Patterns4Net.Tests.PatternEnforcer.Core.Patterns.Regression
{
    using System;
    using NUnit.Framework;
    using Patterns4Net.Core.CecilExtensions;
    using Patterns4Net.Core.Patterns;
    using Patterns4Net.PatternEnforcer.Core.Patterns;

    [TestFixture]
    public class VisitorCheckerTest : PatternCheckerTestBase
    {
        // [Ignore]
        [Test]
        // [TestCase(typeof(EmptyVisitor), typeof(IRoot))]
        // [TestCase(typeof(ICorrectOldSchoolVisitor), typeof(IRoot))]
        // [TestCase(typeof(ICorrectOldSchoolVisitor), typeof(AbstractRoot))]
        [TestCase(typeof(IVisitorThatMissesLeafB), typeof(IRoot))]
        //[TestCase(typeof(IVisitorThatMissesLeafB), typeof(AbstractRoot))]
        //[TestCase(typeof(IVisitorThatIsNotAcceptedByLeafA), typeof(IRoot))]
        //[TestCase(typeof(IVisitorThatIsNotAcceptedByLeafA), typeof(AbstractRoot))]
        //[TestCase(typeof(FileSystemVisitor), typeof(IRoot))]
        //[TestCase(typeof(FileSystemVisitor), typeof(AbstractRoot))]
        public void RunAllTests(Type visitor, Type root)
        {
            var checker = new VisitorChecker();
            this.TestChecker(checker, visitor, new Visitor(visitor.GetTypeDefinition(), root.GetTypeDefinition(), false));
        }

        [PatternTest(false)]
        public class EmptyVisitor
        {            
        }

        [PatternTest(true)]
        public interface ICorrectOldSchoolVisitor
        {
            void Visit(Root item);

            void Visit(LeafA item);

            void Visit(LeafB item);
        }

        [PatternTest(false)]
        public interface IVisitorThatMissesLeafB
        {
            void Visit(Root item);

            void Visit(LeafA item);
        }

        [PatternTest(false)]
        public interface IVisitorThatIsNotAcceptedByLeafA
        {
            void Visit(Root item);

            void Visit(LeafA item);

            void Visit(LeafB item);
        }

        #region Root-Leaf hierarchy

        public interface IRoot
        {
            void Accept(ICorrectOldSchoolVisitor visitor);

            void Accept(IVisitorThatMissesLeafB visitor);

            void Accept(IVisitorThatIsNotAcceptedByLeafA visitor);
        }

        public abstract class AbstractRoot : IRoot
        {
            public abstract void Accept(ICorrectOldSchoolVisitor visitor);

            public abstract void Accept(IVisitorThatMissesLeafB visitor);

            public abstract void Accept(IVisitorThatIsNotAcceptedByLeafA visitor);
        }

        public class Root : AbstractRoot
        {
            public override void Accept(ICorrectOldSchoolVisitor visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatMissesLeafB visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatIsNotAcceptedByLeafA visitor)
            {
                visitor.Visit(this);
            }
        }

        public class LeafA : Root
        {
            public override void Accept(ICorrectOldSchoolVisitor visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatMissesLeafB visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatIsNotAcceptedByLeafA visitor)
            {
                visitor.Visit(this);
            }
        }

        public class LeafB : Root
        {
            public override void Accept(ICorrectOldSchoolVisitor visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatMissesLeafB visitor)
            {
                visitor.Visit(this);
            }

            public override void Accept(IVisitorThatIsNotAcceptedByLeafA visitor)
            {
                visitor.ToString();
            }
        }

        #endregion

        [PatternTest(false)]
        public class FileSystemVisitor
        {
            public void Visit(File file)
            {
            }

            public void Visit(Folder folder)
            {
            }

            public void Visit(Device device)
            {
            }
        }

        #region UNIX like file-directory wrong visiteable hierarchy

        public class File
        {
        }

        public class Folder : File
        {
        }

        public class Device : File
        {
        }

        #endregion
    }
}